﻿/**

 * Copyright (c) 2015-2016, FastDev 刘强 (fastdev@163.com) & Quincy.

 *

 * Licensed under the Apache License, Version 2.0 (the "License");

 * you may not use this file except in compliance with the License.

 * You may obtain a copy of the License at

 *

 *      http://www.apache.org/licenses/LICENSE-2.0

 *

 * Unless required by applicable law or agreed to in writing, software

 * distributed under the License is distributed on an "AS IS" BASIS,

 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

 * See the License for the specific language governing permissions and

 * limitations under the License.

 */

using MsgPack.Serialization;
using OF.Cache2.Entity;
using StackExchange.Redis;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using OF.Cache2.Core;
using OF.Cache2.Entity;
using OF.Cache2.Redis;

namespace OF.Cache2.Impl
{
    public class BusinessLayer2Cache<T> : IOnRedisMessageChange where T : class, ICacheItem
    {
        private byte hostId;
        private int dbIndex;
        private string businessCode = null;
        private int expireSeconds = 0;
        private RedisBusinessHelper redis;

        private FIFODict<T> fifoDict = null;
        MessagePackSerializer<T> serializer = MessagePackSerializer.Get<T>();
        public string MockId
        { get; set; }

        public BusinessLayer2Cache(int maxItemCount, byte hostId, int dbIndex, string businessCode, int expireSeconds)
        {
            fifoDict = new FIFODict<T>(maxItemCount);
            this.hostId = hostId;
            this.dbIndex = dbIndex;
            this.businessCode = businessCode;
            this.expireSeconds = expireSeconds;
            this.redis = new RedisBusinessHelper(hostId, dbIndex, businessCode, this, expireSeconds);
        }

        private void InitExpireDate(T newValue)
        {
            newValue.SetExpireDate(DateTime.Now.Add(TimeSpan.FromSeconds(expireSeconds)));
        }

        public T GetItem(string key, Func<T> getFunc)
        {
            T result = default(T);
            if (fifoDict.TryGetValue(key, out result))
            {
                return result;
            }
            if (RedisHelper.IsConnectionOk())
            {
                try
                {
                    byte[] redisValue = redis.GetBytes(key);
                    if (redisValue != null)
                    {
                        T value = serializer.UnpackSingleObject(redisValue);
                        fifoDict.SetValue(value);
                        //Console.WriteLine(MockId + " Get in redis");
                        return value;
                    }
                }
                catch (StackExchange.Redis.RedisConnectionException ex)
                {

                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex);
                }
            }
            T newValue = getFunc();
            InitExpireDate(newValue);
            if (RedisHelper.IsConnectionOk())
            {
                try
                {
                    byte[] redisValue = serializer.PackSingleObject(newValue);
                    redis.SetBytes(key, redisValue);
                }
                catch (StackExchange.Redis.RedisConnectionException ex)
                {

                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex);
                }
            }
            fifoDict.SetValue(newValue);
            //Console.WriteLine(MockId + " Get by func");
            return newValue;
        }

        public bool SetValue(string key, T item)
        {
            InitExpireDate(item);
            if (RedisHelper.IsConnectionOk())
            {
                try
                {
                    byte[] redisValue = serializer.PackSingleObject(item);
                    redis.SetBytes(key, redisValue);
                }
                catch (StackExchange.Redis.RedisConnectionException ex)
                {

                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex);
                }
            }
            fifoDict.SetValue(item);
            return true;
        }

        public bool Remove(string key)
        {
            fifoDict.Remove(key);
            bool result = false;
            if (RedisHelper.IsConnectionOk())
            {
                try
                {
                    result = redis.Remove(key);
                }
                catch (StackExchange.Redis.RedisConnectionException ex)
                {

                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex);
                }
            }
            return result;
        }

        public void OnMessageRemoved(RedisChannel channel, RedisValue value)
        {
            byte[] bytes = (byte[])value;
            Tuple<string, byte[]> keyData = RedisHelper.SplitKeyData(bytes);
            if (keyData.Item2[0] == this.hostId)
            {
                return;
            }
            Console.WriteLine(MockId + " On Removed " + keyData.Item1);
            fifoDict.Remove(keyData.Item1);
        }

    }

}
